
package com.dg.acb.utils.ipcclient;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;

interface ConnectionManager<T> {
    T getService() throws RemoteException;
    void close();
}

final class ConnectionManagerIml<T> implements ConnectionManager<T> {
    private final Context mContext;
    private final Object mLock = new Object();
    private final StatuMonitor<T> mStatuMonitor;
    private final IServiceHandler<T> mServiceHandler;
    private T mService;

    private final ServiceConnection mServiceConnection = new ServiceConnection() {
        public void onServiceDisconnected(ComponentName name) {
            disconnect();
            mStatuMonitor.onServiceDisconnected();
        }

        public void onServiceConnected(ComponentName name, IBinder binder) {
            synchronized (ConnectionManagerIml.this.mLock) {
                mService = mServiceHandler.onTransact(binder);
                mStatuMonitor.onServiceConnected(mService);
                ConnectionManagerIml.this.mLock.notifyAll();
            }
        }
    };

    ConnectionManagerIml(Context context, IServiceHandler<T> serviceHandler,
            StatuMonitor<T> statuMonitor) {
        mContext = context.getApplicationContext();
        mServiceHandler = serviceHandler;
        mStatuMonitor = statuMonitor;
    }

    private boolean bindService() {
        Intent intent = mServiceHandler.onServiceIntent();
        return intent != null && this.mContext.bindService(
                intent, mServiceConnection,
                Context.BIND_AUTO_CREATE);

    }

    private void unbindService() {
        try {
            mContext.unbindService(mServiceConnection);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    final void disconnect() {
        if(mService != null){
            synchronized (this.mLock) {
                if(mService != null){
                    unbindService();
                    mService = null;
                }
            }
        }
    }

    private void synchronizedLock()
            throws InterruptedException {
        if (this.mService == null) {
            synchronized (this.mLock) {
                if (this.mService == null && bindService()) {
                    this.mLock.wait(5000);
                }
            }
        }
    }

    public final T getService() throws RemoteException {
        if (Looper.myLooper() == Looper.getMainLooper()) {
            throw new IllegalStateException("can not be called from main thread");
        }
        try {
            synchronizedLock();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if (mService != null) {
            return mService;
        }else {
            throw new RemoteException("can't get service");
        }
    }

    @Override
    public void close() {
        disconnect();
    }
}